package main

import (
	"errors"
	"fmt"
	"os"
	"strings"
)

const rows, columns = 9, 9

// Grid is a Sudoku grid
type Grid [rows][columns]int8

// Errors that could occur.
var (
	ErrBounds = errors.New("out of bounds")
	ErrDigit  = errors.New("invalid digit")
)

// SudokuError is a slice of errors.
type SudokuError []error

// Error returns one or more errors separated by commas.
func (se SudokuError) Error() string {
	var s []string
	fmt.Println("se->")
	for _, err := range se {
		s = append(s, err.Error())
	}
	return strings.Join(s, ", ")
}

// 因为error 接口里面有Error方法
// error interface {
//     Error() string
// }
// SudokuError 类型里面也有 Error() 方法，所以 SudokuError 类型是 error接口的实现类
// 所以 此处方法的返回值是 error
// Set a digit on a Sudoku grid
func (g *Grid) Set(row, column int, digit int8) error {
	// 声明一个slice 准备存放所有的error
	var errs SudokuError
	// 如果越界了
	if !inBounds(row, column) {
		errs = append(errs, ErrBounds)
	}
	// 如果参数不合法
	if !validDigit(digit) {
		errs = append(errs, ErrDigit)
	}

	// 判断是否有error发生
	if len(errs) > 0 {
		return errs
	}

	g[row][column] = digit
	return nil
}

// 判断是否在边界内
func inBounds(row, column int) bool {
	if row >= 0 && row < rows && column >= 0 && column < columns {
		return true
	}
	return false
}

// 判断参数是否合法
func validDigit(digit int8) bool {
	return digit >= 1 && digit <= 9
}

// 自定义错误类型
func main() {
	var g Grid
	err := g.Set(10, 0, 15)
	if err != nil {
		// 类型断言 实现了类型转换
		// 将接口类转为了底层具体的实现类，ok 表示成功与否，errs 表示转换后的变量
		if errs, ok := err.(SudokuError); ok {
			// 打印内容
			fmt.Println(errs)
			// 打印类型
			fmt.Printf("%T\n", errs)

			// 便利 slice 打印每一个 元素
			fmt.Printf("%d error(s) occurred:\n", len(errs))
			for _, e := range errs {
				fmt.Printf("- %v\n", e)
			}
		}
		os.Exit(1)
	}
}
